package tt.dz.order.util;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.annotation.PostConstruct;
import javax.net.ssl.SSLContext;

import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.alibaba.druid.util.StringUtils;

import tt.dz.order.dao.SysApplicationContextAware;

/**
 * @title 支付宝支付工具类
 * @author zhoutian
 */
@Component(value = "aliPayUtil")
public class AliPayUtil {
	private static Logger log = LoggerFactory.getLogger(AliPayUtil.class);

	public static final String RET_SUCCESS_STR = "success";

	public static final String RET_FAIL_STR = "fail";

	/** 合作者身份ID，签约的支付宝账号对应的支付宝唯一用户号。以2088开头的16位纯数字组成 */
	@Value(value = "${alipay.partner}")
	public String PARTNER;

	/** 卖家支付宝账号 */
	@Value(value = "${alipay.sellerEmail}")
	public String SELLER_EMAIL;

	/** 卖家用户ID，卖家支付宝账号对应的支付宝唯一用户号。以2088开头的纯16位数字 */
	@Value(value = "${alipay.sellerUserId}")
	public String SELLER_USER_ID;

	/** 商户的私钥 */
	@Value(value = "${alipay.partnerPrivateKey}")
	private String PARTNER_PRIVATE_KEY;

	/** 支付宝的公钥，无需修改该值 */
	private static final String ALI_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCnxj/9qwVfgoUh/y2W89L6BkRAFljhNhgPdyPuBV64bfQNN1PjbCzkIM6qRdKBoLPXmKKMiFYnkd6rAoprih3/PrQEB/VsW8OoM8fxn67UDYuyBTqA23MML9q1+ilIZwBC2AQ2UBVOrFXfFl75p6/B5KsiNG9zpgmLCUYuLkxpLQIDAQAB";

	public static final String INPUT_CHARSET = "UTF-8";

	public static final String SIGN_ALGORITHMS = "SHA1WithRSA";

	public static final String SIGN_TYPE = "RSA";

	/** 支付宝消息验证地址 */
	private static final String HTTPS_VERIFY_URL = "https://mapi.alipay.com/gateway.do?service=notify_verify&";

	/** 支付宝网关地址 */
	private static final String HTTPS_ALIPAY_GATEWAY_URL = "https://mapi.alipay.com/gateway.do";

	/** 数据传输超时时间 */
	private static int socketTimeout = 5 * 1000;

	/** 连接超时时间 */
	private static int connectionTimeout = 5 * 1000;

	/** 连接请求超时时间 */
	private static int connectionRequestTimeout = 5 * 1000;

	@SuppressWarnings("unused")
	private static SSLConnectionSocketFactory sslsf;

	@PostConstruct
	public void init() {
		// 退款流程，需要加载证书，当前不需要退款流程
		FileInputStream instream = null;
		try {
			KeyStore keyStore = KeyStore.getInstance("PKCS12");
			instream = new FileInputStream(new File("MCH_PKCS12_FILEPATH"));
			keyStore.load(instream, "MCH_ID".toCharArray());
			// Trust own CA and all self-signed certs
			SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, "MCH_ID".toCharArray()).build();
			// Allow TLSv1 protocol only
			sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null,
					SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
		} catch (Exception e) {
			log.error("支付宝支付初始化加载KeyStore失败，" + e);
		} finally {
			if (instream != null) {
				try {
					instream.close();
				} catch (IOException e) {
					log.error("支付宝支付初始化加载KeyStore，关闭instream出错" + e);
				}
			}
		}
	}

	/**
	 * 除去数组中的空值和签名参数
	 * 
	 * @param sArray
	 *            签名参数组
	 * @return 去掉空值与签名参数后的新签名参数组
	 */
	public static Map<String, String> paraFilter(Map<String, String> sArray) {
		Map<String, String> result = new HashMap<String, String>();
		if (sArray == null || sArray.size() <= 0) {
			return result;
		}
		String key;
		String value ;
		for (Entry<String, String> entry : sArray.entrySet()) {
			key = entry.getKey();
			value = entry.getValue();
			if (StringUtils.isEmpty(value) || "sign".equalsIgnoreCase(key) || "sign_type".equalsIgnoreCase(key)) {
				// 值为空或者key为"sign"的参数不进行拼接
				continue;
			}
			result.put(key, value);
		}
		return result;
	}

	/**
	 * 把数组所有元素排序，并按照“参数=参数值”的模式用“&”字符拼接成字符串
	 * 
	 * @param params
	 *            需要排序并参与字符拼接的参数组
	 * @return 拼接后字符串
	 */
	public static String createLinkString(Map<String, String> params) {
		List<String> keys = new ArrayList<String>(params.keySet());
		Collections.sort(keys);
		StringBuilder prestr = new StringBuilder();
		for (int i = 0; i < keys.size(); i++) {
			String key = keys.get(i);
			String value = params.get(key);
			if (i == keys.size() - 1) {// 拼接时，不包括最后一个&字符
				prestr = prestr.append(key).append("=").append(value);
			} else {
				prestr = prestr.append(key).append("=").append(value).append("&");
			}
		}
		return prestr.toString();
	}

	/**
	 * 支付宝支付回调验证sign签名
	 * 
	 * @param params
	 * @return true/false
	 * @throws NoSuchAlgorithmException
	 * @throws UnsupportedEncodingException
	 */
	public static boolean verify(Map<String, String> params)  {
		if (params == null) {
			log.error("支付宝支付回调验证sign签名传参为空");
			return false;
		}
		StringBuilder builder = new StringBuilder();
		for (Entry<String, String> entry : params.entrySet()) {
			builder.append("[" + entry.getKey() + " = " + entry.getValue() + "] - ");
		}
		log.info("支付宝支付回调验证sign签名传参" + builder.toString());
		String sign =  params.get("sign").toString();
		if (sign == null) {
			log.error("支付宝支付回调验证sign签名,签名为空");
			return false;
		}
		// 过滤空值、sign与sign_type参数
		Map<String, String> sParaNew = paraFilter(params);
		// 拼接字符串
		String preSignStr = createLinkString(sParaNew);
		return doVerify(preSignStr, sign, ALI_PUBLIC_KEY, INPUT_CHARSET);
	}

	/**
	 * RSA签名检查
	 * 
	 * @param content
	 *            待签名数据
	 * @param sign
	 *            签名值
	 * @param ali_public_key
	 *            支付宝公钥
	 * @param input_charset
	 *            编码格式
	 * @return 布尔值
	 */
	public static boolean doVerify(String content, String sign, String ali_public_key, String input_charset) {
		try {
			KeyFactory keyFactory = KeyFactory.getInstance("RSA");
			byte[] encodedKey = Base64.decode(ali_public_key);
			PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
			java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS);
			signature.initVerify(pubKey);
			signature.update(content.getBytes(input_charset));
			return signature.verify(Base64.decode(sign));
		} catch (Exception e) {
			log.error("支付宝RSA签名检查失败", e);
			return false;
		}
	}

	/**
	 * 获取远程服务器ATN结果,验证返回URL
	 * 
	 * @param notify_id
	 *            通知校验ID
	 * @return 服务器ATN结果 验证结果集： invalid命令参数不对 出现这个错误，请检测返回处理中partner和key是否为空 true
	 *         返回正确信息 false 请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟
	 */
	@SuppressWarnings("unused")
	private static String verifyResponse(String notify_id) {
		// 获取远程服务器ATN结果，验证是否是支付宝服务器发来的请求
		String veryfy_url = HTTPS_VERIFY_URL + "partner=" + SysApplicationContextAware.getBean(AliPayUtil.class).PARTNER
				+ "&notify_id=" + notify_id;
		return checkUrl(veryfy_url);
	}

	/**
	 * 获取远程服务器ATN结果
	 * 
	 * @param urlvalue
	 *            指定URL路径地址
	 * @return 服务器ATN结果 验证结果集： invalid命令参数不对 出现这个错误，请检测返回处理中partner和key是否为空 true
	 *         返回正确信息 false 请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟
	 */
	private static String checkUrl(String urlvalue) {
		String inputLine = "";
		log.info("获取远程服务器ATN传参数-->" + urlvalue);
		try {
			URL url = new URL(urlvalue);
			HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
			BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
			inputLine = in.readLine().toString();
			log.info("获取远程服务器ATN结果 -->" + inputLine);
		} catch (Exception e) {
			log.error("支付宝支付回调，获取远程服务器ATN结果失败", e);
			inputLine = "";
		}
		return inputLine;
	}

	/**
	 * 支付宝生成sign签名
	 * 
	 * @return 签名字符串
	 */
	public static String sign(Map<String, String> params) throws UnsupportedEncodingException {
		// 过滤空值、sign与sign_type参数
		Map<String, String> sParaNew = paraFilter(params);
		// 拼接字符串
		String linkedStr = createLinkString(sParaNew);
		return doSign(linkedStr, SysApplicationContextAware.getBean(AliPayUtil.class).PARTNER_PRIVATE_KEY,
				INPUT_CHARSET);
	}

	/**
	 * RSA签名
	 * 
	 * @param content
	 *            待签名数据
	 * @param privateKey
	 *            商户私钥
	 * @param inputCharset
	 *            编码格式
	 * @return 签名值
	 */
	private static String doSign(String content, String privateKey, String inputCharset) {
		try {
			PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.decode(privateKey));
			KeyFactory keyf = KeyFactory.getInstance("RSA");
			PrivateKey priKey = keyf.generatePrivate(priPKCS8);
			java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS);
			signature.initSign(priKey);
			signature.update(content.getBytes(inputCharset));
			byte[] signed = signature.sign();
			return Base64.encode(signed);
		} catch (Exception e) {
			log.error("有贝RSA签名失败", e);
		}
		return null;
	}

	/**
	 * 支付宝退款发送https请求
	 * 
	 * @param params
	 */
	public static String sendRefundRequest(Map<String, String> paramMap) {
		StringBuilder retStr = new StringBuilder();
		CloseableHttpClient httpclient = null;
		CloseableHttpResponse response = null;
		try {
			httpclient = HttpClients.custom().build();
			HttpPost httpPost = new HttpPost(HTTPS_ALIPAY_GATEWAY_URL);
			// 设置请求和传输超时时间httpGet.setConfig(requestConfig);
			RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout)
					.setConnectTimeout(connectionTimeout).setConnectionRequestTimeout(connectionRequestTimeout).build();
			httpPost.setConfig(requestConfig);
			// 创建参数队列
			List<NameValuePair> formparams = new ArrayList<NameValuePair>();
			for (Entry<String, String> entry : paramMap.entrySet()) {
				formparams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
			}
			UrlEncodedFormEntity requestEntity = new UrlEncodedFormEntity(formparams, INPUT_CHARSET);
			httpPost.setEntity(requestEntity);
			response = httpclient.execute(httpPost);
			int responseStatus = response.getStatusLine().getStatusCode();
			if (responseStatus != HttpStatus.SC_OK) {
				log.error("支付宝退款发送https请求，响应状态不正确，responseStatus：" + responseStatus);
				return retStr.toString();
			}
			HttpEntity entity = response.getEntity();
			if (entity != null) {
				InputStreamReader isr = new InputStreamReader(entity.getContent(), INPUT_CHARSET);
				BufferedReader bufferedReader = new BufferedReader(isr);
				String text;
				while ((text = bufferedReader.readLine()) != null) {
					retStr.append(text);
				}
				bufferedReader.close();
				isr.close();
			}
			EntityUtils.consume(entity);
		} catch (UnsupportedOperationException e) {
			log.error("支付宝退款发送https请求出错，" + e);
		} catch (UnsupportedEncodingException e) {
			log.error("支付宝退款发送https请求出错，" + e);
		} catch (IOException e) {
			log.error("支付宝退款发送https请求出错，" + e);
		} finally {
			try {
				if (response != null) {
					response.close();
				}
				if (httpclient != null) {
					httpclient.close();
				}
			} catch (IOException e) {
				log.error("支付宝退款发送https请求,关闭httpclient出错" + e);
			}
		}
		return retStr.toString();
	}
}